Skip to content

fix(engine): ignore benign media request aborts#2423

Merged
miguel-heygen merged 1 commit into
mainfrom
fix/probe-media-abort-diagnostics
Jul 16, 2026
Merged

fix(engine): ignore benign media request aborts#2423
miguel-heygen merged 1 commit into
mainfrom
fix/probe-media-abort-diagnostics

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • suppress Chromium net::ERR_ABORTED diagnostics for media requests cancelled during probe/seek
  • preserve real media failures and all non-media request failures
  • cover Puppeteer media classification plus extension fallback, including .oga

Root cause

The capture session appended every requestfailed event to its browser diagnostic buffer. Chromium commonly aborts media requests while the probe discovers or seeks audio/video, and producer probe treated any buffered net::ERR_* line as an asset-load warning even when the final media rendered correctly.

Regression coverage

  • red/green unit test for benign media aborts and real failures
  • @hyperframes/engine targeted test: 20/20
  • @hyperframes/engine typecheck
  • producer probe-stage test: 18/18
  • producer full unit lanes: 295 Vitest + all Bun lanes passed
  • format, lint, diff check, pre-commit fallow/typecheck passed
  • full engine suite: 887 passed; 3 pre-existing LFS HDR-fixture failures in this smudge-disabled worktree

Review

Independent review caught missing .oga fallback coverage; fixed and re-reviewed with no remaining findings.

Reviewer action: verify benign abort suppression does not hide real media/network failures, then approve if CI is green.

@vanceingalls vanceingalls left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strengths. Precise classifier: frameCapture.ts:352 gates on exact net::ERR_ABORTED string match — not a fuzzy .includes('abort') — and requires either resourceType === "media" OR a media-extension pathname match. Anything else falls through and is still buffered. Scope is diagnostic-buffer only (appendBrowserDiagnostic) — the underlying request lifecycle, recordScriptLoadFailure at frameCapture.ts:1610, and downstream media-element error events are all untouched. That's the right narrow surface for "suppress the noise, keep the signal."

Handler ordering at frameCapture.ts:1604-1620 correctly runs recordScriptLoadFailure before the ignore gate, so a script failure (regardless of error kind) is still recorded even if the diagnostic append is later gated.

Findings.

important — sibling helper drift. shouldIgnoreRequestFailure already exists at packages/cli/src/commands/validate.ts:76 with the SAME classifier logic (errorText === "net::ERR_ABORTED" AND (resourceType === "media" OR media-extension pathname)), used at validate.ts:458 and packages/cli/src/utils/checkBrowser.ts:224. This PR adds a second implementation with a slightly different signature and — critically — a divergent extension list: the new regex frameCapture.ts:361 adds ogv, but MEDIA_EXTENSIONS at validate.ts:47 does not. Two implementations that were already at risk of drift are now demonstrably drifted on their first landing. @hyperframes/cli already imports @hyperframes/engine (workspace:*), so consolidation is architecturally clean: promote a single shouldIgnoreRequestFailure(url, errorText, resourceType?) from frameCapture.ts (or a shared engine util) and have validate.ts + checkBrowser.ts import it. Follow-up ticket at minimum.

important — .ogv coverage gap in CLI. Concrete manifestation of the drift above: an Ogg Theora media abort will now be silenced on the engine's diagnostic buffer but flagged as Failed to load ... by hf validate and hf check-browser. That's a divergent operator-facing signal for the same underlying event.

nit — missing .ogv regression test. frameCapture.test.ts:225-256 covers media resourceType, .oga extension fallback, non-ABORTED error, and non-media resourceType, but no case pins .ogv — the ONLY extension this PR added over the CLI sibling. Add one assertion so a future accidental removal is caught.

nit — malformed-URL branch untested. The try/catch at frameCapture.ts:363-367 returns false on new URL() failure, which is the correct conservative default, but nothing exercises it. One line of test coverage buys future refactor safety.

Verdict: APPROVE
Reasoning: The fix itself is right-shaped and CI-green (36/36 required checks pass). The classifier is precise, the funnel invariant is preserved (suppression is at the diagnostic-buffer layer, not the load-completion layer), and regression tests cover the branch matrix. The duplication is a real drift bug seed but doesn't gate merge — worth a follow-up to consolidate before the two extension lists diverge further.

— Via

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at b96f0e0d03. Traced the abort-classification path against the frameCapture observability + retry seam.

Verdict framing

Tight, well-scoped fix. Classification is strict — !== exact string match on net::ERR_ABORTED, not a regex, so no shape-matching false positives. The suppression is at the observability layer only (appendBrowserDiagnostic at frameCapture.ts:1612); script fail-fast still fires because recordScriptLoadFailure (line 1608-1610) runs before the ignore check, and scripts never satisfy the media/extension gate anyway (lines 349-363). Ship.

Blockers

(none)

Concerns

See inline for anchored detail.

  • 🟡 C1 (low-medium) URL-extension fallback at frameCapture.ts:355-359 over-scopes vs the docstring. The comment claims the case is "media loads Chromium probes", but the code silences ERR_ABORTED for any resourceType (including "other") whose URL pathname ends in a media extension. A user-code fetch('/asset.mp4') that legitimately aborts, or an XHR/preload fetch cancelled by a scheduling bug, would be silently dropped. Test at frameCapture.test.ts:236-243 codifies this behavior — probably Miguel's intent given Chromium's inconsistent tagging of MediaSource-initiated fetches, but code-vs-doc mismatch.
  • 🟡 C2 (low) Abort event is dropped entirely from browserConsoleBuffer (the 200-slot circular buffer at frameCapture.ts:225-235 that downstream failure diagnostics dump). If a subsequent render failure needs to correlate "the audio never loaded" against a probe abort, the trail is gone. A debug-level breadcrumb (marker in the buffer without surfacing at diagnostic level) would preserve the observability seam without producing noise.

Nits

  • N1 Test only exercises the pure classification function at frameCapture.test.ts:225-256 — not the request.failure()?.errorText ?? "unknown" fallback at frameCapture.ts:1607, nor the wiring path. Real Puppeteer aborts during teardown can return null from request.failure()"unknown" → NOT ignored → noise diagnostic. Worth one integration test that hits that shape.
  • N2 Extension list at frameCapture.ts:357 omits .opus, .m4v, .3gp, .mkv. Low priority — the resourceType === "media" branch catches these when Chromium classifies them correctly, but a bare-fetch .opus would still spam.
  • N3 Regex is properly anchored ($) and case-insensitive, non-capturing group — clean.

What I didn't verify

  • Whether Chromium's net::ERR_ABORTED can be raised by a network-side failure the user would want to see (my read of Chromium net_error_list.h: ERR_ABORTED is caller-initiated, so the filter is safe — but I didn't cross-check Puppeteer's translation layer).
  • Whether Puppeteer's resourceType() returns "media" reliably for <video>/<audio> src loads across the Chromium versions we ship — browserManager.ts pin not checked.
  • The retry loop — I confirmed the fix is observability-layer-only (no resolution-path branching), but did not trace the full frame-capture retry state machine to rule out a caller inspecting browserConsoleBuffer for REQUESTFAILED markers to decide retry-vs-abort.
  • Whether any dashboard / log aggregator counts [Browser:REQUESTFAILED] markers and would silently see a drop after this ships.

Merge gate

All required checks green per gh pr checks. Nothing merge-blocking. LGTM from my side, COMMENTED.

Review by Rames D Jusso

if (input.failureText !== "net::ERR_ABORTED") return false;
if (input.resourceType === "media") return true;
try {
return /\.(?:aac|flac|m4a|mp3|mp4|mov|oga|ogg|ogv|wav|webm)$/i.test(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 C1: URL-extension fallback over-scopes vs the docstring intent.

The comment says the case is 'media loads Chromium probes', but the code silences ERR_ABORTED for any resourceType (including "other") whose URL pathname ends in a media extension. The test at frameCapture.test.ts:236-243 codifies this — an other-typed request with URL ending in .mp4 gets the ignore treatment.

Hostile counterexample: user-code fetch('/asset.mp4') that legitimately aborts (network stall, scheduling bug), or an XHR / preload fetch cancelled by unrelated code. Both would be silently dropped even though they're not media probes.

Probably Miguel's intent given how inconsistently Chromium tags MediaSource-initiated fetches, but code-vs-comment mismatch. Two options:

  1. Tighten the code: resourceType === 'media' || resourceType === 'image' gate on the extension check.
  2. Update the comment to acknowledge the broader scope ('any request with a media file extension, since Chromium mis-tags MediaSource-initiated fetches').

Low-med severity — this is an observability filter, not a correctness gate. — Review by Rames D Jusso

recordScriptLoadFailure(session, request.url());
}
if (shouldIgnoreRequestFailureDiagnostic({ resourceType, url, failureText })) return;
appendBrowserDiagnostic(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 C2: Abort dropped entirely from browserConsoleBuffer.

Early-return at line 1612 skips the appendBrowserDiagnostic(...) call — but that also skips writing to browserConsoleBuffer (the 200-slot circular buffer at 225-235 that downstream failure diagnostics dump on a real render failure).

Failure scenario: user reports "my render is missing an audio track", ops looks at the dumped browser console buffer, sees no ERR_ABORTED trail because we suppressed it — no visible correlation between the probe abort and the missing track.

Suggest: preserve a debug-level breadcrumb in the buffer (e.g. an appendBrowserDiagnostic(..., { level: 'debug' }) variant that lands in the buffer but doesn't surface at the diagnostic level). Keeps the observability seam intact for post-hoc correlation.

Not blocking — the current behavior is a strict superset of "we no longer noise-spam on probe aborts", which is what the PR promises. — Review by Rames D Jusso

@miguel-heygen
miguel-heygen merged commit 08dbb7d into main Jul 16, 2026
73 checks passed
@miguel-heygen
miguel-heygen deleted the fix/probe-media-abort-diagnostics branch July 16, 2026 22:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants